package com.tsfyun.scm.service.impl.finance;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.tsfyun.common.base.dto.FileDTO;
import com.tsfyun.common.base.dto.TaskDTO;
import com.tsfyun.common.base.enums.SerialNumberTypeEnum;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.DomainTypeEnum;
import com.tsfyun.common.base.enums.domain.OverseasReceivingAccountStatusEnum;
import com.tsfyun.common.base.enums.finance.ReceivingModeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.vo.CustomerSimpleVO;
import com.tsfyun.scm.dto.finance.ClaimedAmountPlusDTO;
import com.tsfyun.scm.dto.finance.ConfirmOverseasReceivingAccountDTO;
import com.tsfyun.scm.dto.finance.OverseasReceivingAccountDTO;
import com.tsfyun.scm.dto.finance.OverseasReceivingAccountQTO;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.dto.system.SubjectOverseasQTO;
import com.tsfyun.scm.entity.base.Currency;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.finance.OverseasReceivingAccount;
import com.tsfyun.scm.entity.system.Subject;
import com.tsfyun.scm.entity.system.SubjectBank;
import com.tsfyun.scm.entity.system.SubjectOverseas;
import com.tsfyun.scm.entity.system.SubjectOverseasBank;
import com.tsfyun.scm.enums.SubjectTypeEnum;
import com.tsfyun.scm.enums.UndertakingModeEnum;
import com.tsfyun.scm.mapper.finance.OverseasReceivingAccountMapper;
import com.tsfyun.scm.service.base.ICurrencyService;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.file.IUploadFileService;
import com.tsfyun.scm.service.finance.IOverseasReceivingAccountService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.finance.IOverseasReceivingOrderService;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.*;
import com.tsfyun.scm.vo.finance.*;
import com.tsfyun.scm.vo.report.ExpOneVoteTheEndVO;
import com.tsfyun.scm.vo.system.SubjectOverseasVO;
import com.tsfyun.scm.vo.system.SubjectVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 境外收款 服务实现类
 * </p>
 *
 *
 * @since 2021-10-08
 */
@Service
public class OverseasReceivingAccountServiceImpl extends ServiceImpl<OverseasReceivingAccount> implements IOverseasReceivingAccountService {

    @Autowired
    private OverseasReceivingAccountMapper overseasReceivingAccountMapper;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ICurrencyService currencyService;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private IStatusHistoryService statusHistoryService;
    @Autowired
    private ISubjectOverseasBankService subjectOverseasBankService;
    @Autowired
    private ISubjectBankService subjectBankService;
    @Autowired
    private ISubjectService subjectService;
    @Autowired
    private ISubjectOverseasService subjectOverseasService;
    @Autowired
    private IUploadFileService uploadFileService;
    @Autowired
    private IOverseasReceivingOrderService overseasReceivingOrderService;
    @Autowired
    private IExpOrderService expOrderService;


    @Override
    public PageInfo<OverseasReceivingAccountVO> pageList(OverseasReceivingAccountQTO qto) {
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        List<OverseasReceivingAccountVO> list = overseasReceivingAccountMapper.list(params);
        //获取客户名称
        Set<Long> customerIds = list.stream().map(OverseasReceivingAccountVO::getCustomerId).collect(Collectors.toSet());
        Map<Long, CustomerSimpleVO> customerMap = customerService.obtainCustomerSimples(customerIds);
        list.stream().forEach(data->{
            data.setCustomerName(Optional.ofNullable(customerMap.get(data.getCustomerId())).map(CustomerSimpleVO::getName).orElse(""));
        });
        return new PageInfo<>(list);
    }

    @Override
    public OverseasReceivingAccountPlusVO detail(Long id, String operation) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation),overseasReceivingAccount.getStatusId());
        Customer customer = customerService.getById(overseasReceivingAccount.getCustomerId());
        OverseasReceivingAccountVO receivingAccountVO = beanMapper.map(overseasReceivingAccount,OverseasReceivingAccountVO.class);
        receivingAccountVO.setCustomerName(customer.getName());
        OverseasReceivingAccountPlusVO vo = new OverseasReceivingAccountPlusVO();
        vo.setAccount(receivingAccountVO);
        // 查询认领订单
        vo.setOrders(overseasReceivingOrderService.findByAccountId(overseasReceivingAccount.getId()));
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(OverseasReceivingAccountDTO dto) {
        Customer customer = customerService.findByNameWithRight(dto.getCustomerName());
        OverseasReceivingAccount overseasReceivingAccount = beanMapper.map(dto,OverseasReceivingAccount.class);
        overseasReceivingAccount.setCustomerId(customer.getId());
        SubjectTypeEnum subjectType = SubjectTypeEnum.of(overseasReceivingAccount.getSubjectType());
        switch (subjectType){
            case ABROAD:
                // 境外主体
                SubjectOverseas subjectOverseas = subjectOverseasService.getById(overseasReceivingAccount.getOverseasId());
                Optional.ofNullable(subjectOverseas).orElseThrow(()->new ServiceException("境外收款主体不存在"));
                overseasReceivingAccount.setOverseasName(subjectOverseas.getName());
                // 境外银行
                SubjectOverseasBank subjectOverseasBank = subjectOverseasBankService.getById(dto.getOverseasBankId());
                Optional.ofNullable(subjectOverseasBank).orElseThrow(()->new ServiceException("境外收款银行不存在"));
                overseasReceivingAccount.setReceivingBank(subjectOverseasBank.getName());
                overseasReceivingAccount.setReceivingBankNo(subjectOverseasBank.getAccount());
                break;
            case MAINLAND:
                // 境内主体
                Subject subject = subjectService.getById(overseasReceivingAccount.getOverseasId());
                Optional.ofNullable(subject).orElseThrow(()->new ServiceException("境内收款主体不存在"));
                overseasReceivingAccount.setOverseasName(subject.getName());
                // 境内银行
                SubjectBank subjectBank = subjectBankService.getById(dto.getOverseasBankId());
                Optional.ofNullable(subjectBank).orElseThrow(()->new ServiceException("境内收款银行不存在"));
                overseasReceivingAccount.setReceivingBank(subjectBank.getName());
                overseasReceivingAccount.setReceivingBankNo(subjectBank.getAccount());
                break;

            default:
                throw new ServiceException("收款主体类型错误");
        }
        Currency currency = currencyService.findByName(dto.getCurrencyName());
        Optional.ofNullable(currency).orElseThrow(()->new ServiceException("币制错误"));
        overseasReceivingAccount.setCurrencyId(currency.getId());
        overseasReceivingAccount.setCurrencyName(currency.getName());
        String docNo = serialNumberService.generateDocNo(SerialNumberTypeEnum.OVERSEAS_RECEIVING_ACCOUNT);
        overseasReceivingAccount.setDocNo(docNo);
        overseasReceivingAccount.setWriteValue(BigDecimal.ZERO);
        overseasReceivingAccount.setFee(BigDecimal.ZERO);
        OverseasReceivingAccountStatusEnum statusEnum = OverseasReceivingAccountStatusEnum.WAIT_CONFIRM;
        overseasReceivingAccount.setStatusId(statusEnum.getCode());
        overseasReceivingAccount.setReceivingMode(ReceivingModeEnum.CASH.getCode());
        try {
            super.saveNonNull(overseasReceivingAccount);
        } catch (DuplicateKeyException e) {
            if (e.getMessage().contains("UK_overseas_receiving_account_docNo")) {
                throw new ServiceException("境外收款单号已经存在，请稍后重试");
            } else {
                throw new ServiceException("保存失败，请检查填写数据是否合法");
            }
        }
        //关联文件信息
        FileDTO fileDTO = new FileDTO();
        fileDTO.setDocId(dto.getFileId());
        fileDTO.setDocType("overseas_receiving_account");
        uploadFileService.relateFile(overseasReceivingAccount.getId().toString(),fileDTO);
        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_REGISTER,
                overseasReceivingAccount.getId().toString(), OverseasReceivingAccount.class.getName(),
                statusEnum.getCode(),statusEnum.getName(),"收款登记");
        //发送任务通知
        notice(customer,overseasReceivingAccount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(OverseasReceivingAccountDTO dto) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(dto.getId());
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        DomainOprationEnum oprationEnum = DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_EDIT;
        DomainStatus.getInstance().check(oprationEnum,overseasReceivingAccount.getStatusId());
        Customer customer = customerService.findByNameWithRight(dto.getCustomerName());
        overseasReceivingAccount.setCustomerId(customer.getId());
        SubjectTypeEnum subjectType = SubjectTypeEnum.of(dto.getSubjectType());
        switch (subjectType){
            case ABROAD:
                // 境外主体
                SubjectOverseas subjectOverseas = subjectOverseasService.getById(dto.getOverseasId());
                Optional.ofNullable(subjectOverseas).orElseThrow(()->new ServiceException("境外收款主体不存在"));
                overseasReceivingAccount.setOverseasName(subjectOverseas.getName());
                // 境外银行
                SubjectOverseasBank subjectOverseasBank = subjectOverseasBankService.getById(dto.getOverseasBankId());
                Optional.ofNullable(subjectOverseasBank).orElseThrow(()->new ServiceException("境外收款银行不存在"));
                overseasReceivingAccount.setReceivingBank(subjectOverseasBank.getName());
                overseasReceivingAccount.setReceivingBankNo(subjectOverseasBank.getAccount());
                break;
            case MAINLAND:
                // 境内主体
                Subject subject = subjectService.getById(dto.getOverseasId());
                Optional.ofNullable(subject).orElseThrow(()->new ServiceException("境内收款主体不存在"));
                overseasReceivingAccount.setOverseasName(subject.getName());
                // 境内银行
                SubjectBank subjectBank = subjectBankService.getById(dto.getOverseasBankId());
                Optional.ofNullable(subjectBank).orElseThrow(()->new ServiceException("境内收款银行不存在"));
                overseasReceivingAccount.setReceivingBank(subjectBank.getName());
                overseasReceivingAccount.setReceivingBankNo(subjectBank.getAccount());
                break;

            default:
                throw new ServiceException("收款主体类型错误");
        }
        Currency currency = currencyService.findByName(dto.getCurrencyName());
        Optional.ofNullable(currency).orElseThrow(()->new ServiceException("币制错误"));
        overseasReceivingAccount.setCurrencyId(currency.getId());
        overseasReceivingAccount.setCurrencyName(currency.getName());
        overseasReceivingAccount.setPayer(dto.getPayer());
        overseasReceivingAccount.setPayerBank(dto.getPayerBank());
        overseasReceivingAccount.setPayerBankNo(dto.getPayerBankNo());
        overseasReceivingAccount.setAccountValue(dto.getAccountValue());
        overseasReceivingAccount.setMemo(dto.getMemo());
        try {
            super.updateById(overseasReceivingAccount);
        } catch (DuplicateKeyException e) {
            if (e.getMessage().contains("UK_overseas_receiving_account_docNo")) {
                throw new ServiceException("境外收款单号已经存在，请稍后重试");
            } else {
                throw new ServiceException("保存失败，请检查填写数据是否合法");
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void confirm(ConfirmOverseasReceivingAccountDTO dto) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(dto.getId());
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        DomainOprationEnum oprationEnum = DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CONFIRM;
        DomainStatus.getInstance().check(oprationEnum,overseasReceivingAccount.getStatusId());
        OverseasReceivingAccountStatusEnum historyStatus = OverseasReceivingAccountStatusEnum.of(overseasReceivingAccount.getStatusId());
        OverseasReceivingAccountStatusEnum nowStatusEnum = OverseasReceivingAccountStatusEnum.WAIT_CLAIM;
        overseasReceivingAccount.setStatusId(nowStatusEnum.getCode());
        overseasReceivingAccount.setAccountDate(dto.getAccountDate());
        overseasReceivingAccount.setUndertakingMode(dto.getUndertakingMode());
        overseasReceivingAccount.setFee(dto.getFee());
        super.updateById(overseasReceivingAccount);
        // 记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CONFIRM,
                overseasReceivingAccount.getId().toString(), OverseasReceivingAccount.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                nowStatusEnum.getCode(),nowStatusEnum.getName(),"财务确定境外收款到账"
        );
        // 清空任务
        clearTaskNotice(overseasReceivingAccount.getId());
        // 发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.OVERSEASRECEIVINGACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(overseasReceivingAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(overseasReceivingAccount.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CLAIM.getCode());
        taskNoticeContentDTO.setContent(String.format("境外收款单【%s】财务已确定到账，请尽快完成认领。",overseasReceivingAccount.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",overseasReceivingAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void claim(ClaimedAmountPlusDTO dto) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(dto.getId());
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        DomainOprationEnum oprationEnum = DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CLAIM;
        DomainStatus.getInstance().check(oprationEnum,overseasReceivingAccount.getStatusId());
        OverseasReceivingAccountStatusEnum historyStatus = OverseasReceivingAccountStatusEnum.of(overseasReceivingAccount.getStatusId());
        OverseasReceivingAccountStatusEnum nowStatusEnum = OverseasReceivingAccountStatusEnum.WAIT_CLAIM;
        // 认领到订单
        BigDecimal totalClaimed = expOrderService.overseasAccountClaimed(dto);
        BigDecimal surplus = overseasReceivingAccount.getAccountValue().subtract(overseasReceivingAccount.getWriteValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        // 如果是客户承担需要扣除手续费
        if(Objects.equals(UndertakingModeEnum.CUSTOMER,UndertakingModeEnum.of(overseasReceivingAccount.getUndertakingMode()))){
            surplus = surplus.subtract(overseasReceivingAccount.getFee()).setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        if(totalClaimed.compareTo(surplus)==1){
            // 认领金额大于收款单可认领金额
            throw new ServiceException(String.format("当前收款单本次最多可认领金额为：%s，您勾选的认领金额为：%s",StringUtils.formatCurrency(surplus),StringUtils.formatCurrency(totalClaimed)));
        }
        if(totalClaimed.compareTo(surplus)==0){
            // 收款单全部认领完成
            nowStatusEnum = OverseasReceivingAccountStatusEnum.COMPLETE;
            // 清空任务
            clearTaskNotice(overseasReceivingAccount.getId());
        }
        overseasReceivingAccount.setWriteValue(overseasReceivingAccount.getWriteValue().add(totalClaimed).setScale(2,BigDecimal.ROUND_HALF_UP));
        overseasReceivingAccount.setStatusId(nowStatusEnum.getCode());
        List<String> orderNos = overseasReceivingOrderService.findOrderNo(overseasReceivingAccount.getId());
        overseasReceivingAccount.setOrderNo(String.join(",",orderNos));
        List<String> clientNos = expOrderService.findClientNoByDocNos(orderNos);
        overseasReceivingAccount.setClientNo(String.join(",",clientNos));
        super.updateById(overseasReceivingAccount);
        // 记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CONFIRM,
                overseasReceivingAccount.getId().toString(), OverseasReceivingAccount.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                nowStatusEnum.getCode(),nowStatusEnum.getName(),"订单认领"
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearClaimOrder(Long id, DomainOprationEnum oprationEnum) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        if(Objects.nonNull(oprationEnum)){
            DomainStatus.getInstance().check(oprationEnum,overseasReceivingAccount.getStatusId());
        }
        // 清空明细和订单数据
        overseasReceivingOrderService.clearClaimOrder(overseasReceivingAccount);
        overseasReceivingAccount.setWriteValue(BigDecimal.ZERO);
        overseasReceivingAccount.setOrderNo("");
        overseasReceivingAccount.setClientNo("");
        super.updateById(overseasReceivingAccount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void toVoid(TaskDTO dto) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(dto.getDocumentId());
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        if(Objects.equals(overseasReceivingAccount.getIsPayment(),Boolean.TRUE)){
            throw new ServiceException("当前收款单已经完成付款无法作废");
        }
        if(Objects.equals(overseasReceivingAccount.getIsSettlement(),Boolean.TRUE)){
            throw new ServiceException("当前收款单已经结汇请先删除结汇单");
        }
        // 清空任务通知
        clearTaskNotice(overseasReceivingAccount.getId());
        OverseasReceivingAccountStatusEnum historyStatus = OverseasReceivingAccountStatusEnum.of(overseasReceivingAccount.getStatusId());
        OverseasReceivingAccountStatusEnum nowStatusEnum = OverseasReceivingAccountStatusEnum.INVALID;
        overseasReceivingAccount.setStatusId(nowStatusEnum.getCode());
        super.updateById(overseasReceivingAccount);
        // 记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CANCEL,
                overseasReceivingAccount.getId().toString(), OverseasReceivingAccount.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                nowStatusEnum.getCode(),nowStatusEnum.getName(),dto.getMemo()
        );
        // 清空收款认领数据
        clearClaimOrder(overseasReceivingAccount.getId(),null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeAllById(Long id) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        DomainOprationEnum oprationEnum = DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_REMOVE;
        DomainStatus.getInstance().check(oprationEnum,overseasReceivingAccount.getStatusId());
        //删除历史状态
        statusHistoryService.removeHistory(id.toString(), OverseasReceivingAccount.class.getName());
        super.removeById(id);
    }

    @Override
    public List<SubjectTypeVO> subjectSelect(String first) {
        List<SubjectTypeVO> mainlandList = new ArrayList<>();
        // 获取主体
        SubjectVO subjectVO = subjectService.findByCode();
        mainlandList.add(new SubjectTypeVO(subjectVO.getId(),subjectVO.getName(), SubjectTypeEnum.MAINLAND.getCode()));
        List<SubjectTypeVO> abroadList = new ArrayList<>();
        SubjectOverseasQTO qto = new SubjectOverseasQTO();
        qto.setDisabled(Boolean.FALSE);
        List<SubjectOverseasVO> subjectOverseasVOList = subjectOverseasService.findList(qto);
        for(SubjectOverseasVO overseasVO : subjectOverseasVOList){
            abroadList.add(new SubjectTypeVO(overseasVO.getId(),overseasVO.getName(), SubjectTypeEnum.ABROAD.getCode()));
        }
        if(SubjectTypeEnum.MAINLAND.getCode().equals(first)){
            mainlandList.addAll(abroadList);
            return mainlandList;
        }else{
            abroadList.addAll(mainlandList);
            return abroadList;
        }
    }

    @Override
    public SettlementPlusVO initSettlement(List<Long> ids) {
        SettlementAccountVO settlementAccountVO = new SettlementAccountVO();
        settlementAccountVO.setAccountValue(BigDecimal.ZERO);
        // 收款单号
        List<String> accountNos = new ArrayList<>();
        // 收款单
        List<OverseasReceivingAccountVO> overseasReceivingAccountList = new ArrayList<>();
        for(Long id : ids){
            OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
            Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
            if(!Objects.equals(OverseasReceivingAccountStatusEnum.COMPLETE,OverseasReceivingAccountStatusEnum.of(overseasReceivingAccount.getStatusId()))){
                throw new ServiceException(String.format("收款单【%s】不处于已完成状态无法申请结汇",overseasReceivingAccount.getDocNo()));
            }
            if(Objects.equals(overseasReceivingAccount.getIsSettlement(),Boolean.TRUE)){
                throw new ServiceException(String.format("收款单【%s】已完成结汇请勿重复申请",overseasReceivingAccount.getDocNo()));
            }
            accountNos.add(overseasReceivingAccount.getDocNo());
            // 原币金额
            settlementAccountVO.setAccountValue(settlementAccountVO.getAccountValue().add(overseasReceivingAccount.getAccountValue()).subtract(overseasReceivingAccount.getFee()).setScale(2,BigDecimal.ROUND_HALF_UP));
            if(StringUtils.isEmpty(settlementAccountVO.getCurrencyId())){
                settlementAccountVO.setCurrencyId(overseasReceivingAccount.getCurrencyId());
                settlementAccountVO.setCurrencyName(overseasReceivingAccount.getCurrencyName());
            }else{
                if(!settlementAccountVO.getCurrencyId().equals(overseasReceivingAccount.getCurrencyId())){
                    throw new ServiceException("不同收款单币制不能合并申请结汇");
                }
            }
            OverseasReceivingAccountVO overseasReceivingAccountVO = beanMapper.map(overseasReceivingAccount,OverseasReceivingAccountVO.class);
            Customer customer = customerService.getById(overseasReceivingAccount.getCustomerId());
            overseasReceivingAccountVO.setCustomerName(customer.getName());
            overseasReceivingAccountList.add(overseasReceivingAccountVO);
        }
        settlementAccountVO.setAccountNo(String.join(",",accountNos));
        settlementAccountVO.setSettlementDate(LocalDateTime.now());
        SettlementPlusVO result = new SettlementPlusVO();
        result.setSettlementAccount(settlementAccountVO);
        result.setOverseasReceivingAccountList(overseasReceivingAccountList);
        return result;
    }

    @Override
    public CreatePaymentVO initPayment(Long id) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
        Optional.ofNullable(overseasReceivingAccount).orElseThrow(()->new ServiceException("境外收款信息不存在"));
        if(!Objects.equals(OverseasReceivingAccountStatusEnum.COMPLETE,OverseasReceivingAccountStatusEnum.of(overseasReceivingAccount.getStatusId()))){
            throw new ServiceException(String.format("收款单【%s】不处于已完成状态无法申请付款",overseasReceivingAccount.getDocNo()));
        }
        if(Objects.equals(overseasReceivingAccount.getIsSettlement(),Boolean.FALSE)){
            throw new ServiceException(String.format("收款单【%s】未完成结汇无法做付款申请",overseasReceivingAccount.getDocNo()));
        }
        if(Objects.equals(overseasReceivingAccount.getIsPayment(),Boolean.TRUE)){
            throw new ServiceException(String.format("收款单【%s】已申请付款申请请勿重复申请",overseasReceivingAccount.getDocNo()));
        }
        CreatePaymentVO result = new CreatePaymentVO();
        result.setOverseasAccountId(overseasReceivingAccount.getId());
        result.setCustomerId(overseasReceivingAccount.getCustomerId());
        Customer customer = customerService.getById(overseasReceivingAccount.getCustomerId());
        result.setCustomerName(customer.getName());
        result.setPayeeName(customer.getName());
        result.setOriginalAccountValue(overseasReceivingAccount.getAccountValue().subtract(overseasReceivingAccount.getFee()).setScale(2,BigDecimal.ROUND_HALF_UP));
        result.setCurrencyName(overseasReceivingAccount.getCurrencyName());
        result.setAccountValue(overseasReceivingAccount.getSettlementValue());
        result.setOrderNo(overseasReceivingAccount.getOrderNo());
        result.setClientNo(overseasReceivingAccount.getClientNo());
        result.setAccountNo(overseasReceivingAccount.getDocNo());
        return result;
    }

    @Override
    public List<OverseasReceivingOrderVO> overseasReceivings(List<String> accountNos) {
        return overseasReceivingOrderService.overseasReceivings(accountNos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeSettlementValue(Long id, String settlementNo, BigDecimal settlementValue,Long settlementId,BigDecimal customsRate) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
        overseasReceivingAccount.setSettlementNo(settlementNo);
        overseasReceivingAccount.setSettlementValue(settlementValue);
        overseasReceivingAccount.setIsSettlement(StringUtils.isNotEmpty(overseasReceivingAccount.getSettlementNo()));
        overseasReceivingAccount.setSettlementId(settlementId);
        overseasReceivingAccount.setCustomsRate(customsRate);
        super.updateById(overseasReceivingAccount);
        // 写入订单
        List<OverseasReceivingOrderVO> overseasReceivingOrderVOList = overseasReceivingOrderService.findByAccountId(overseasReceivingAccount.getId());
        BigDecimal totalSettlementValue = BigDecimal.ZERO;
        for(OverseasReceivingOrderVO oraVO : overseasReceivingOrderVOList){
            oraVO.setSettlementValue(oraVO.getAccountValue().divide(overseasReceivingAccount.getWriteValue(),20,BigDecimal.ROUND_HALF_UP).multiply(settlementValue).setScale(2,BigDecimal.ROUND_HALF_UP));
            totalSettlementValue = totalSettlementValue.add(oraVO.getSettlementValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        // 防止分摊不均匀
        overseasReceivingOrderVOList.get(0).setSettlementValue(
                overseasReceivingOrderVOList.get(0).getSettlementValue().add(settlementValue.subtract(totalSettlementValue)).setScale(2,BigDecimal.ROUND_HALF_UP)
        );
        for(OverseasReceivingOrderVO oraVO : overseasReceivingOrderVOList){
            expOrderService.writeSettlementValue(oraVO.getExpOrderId(),oraVO.getAccountValue(),oraVO.getSettlementValue());
            // 写入收款单明细结汇人民币金额
            overseasReceivingOrderService.writeSettlementValue(oraVO.getId(),oraVO.getSettlementValue());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void canWriteSettlementValue(Long id, BigDecimal settlementValue) {
        OverseasReceivingAccount overseasReceivingAccount = super.getById(id);
        if(Objects.equals(overseasReceivingAccount.getIsPayment(),Boolean.TRUE)){
            throw new ServiceException(String.format("收款单【%s】已完成付款无法执行此操作",overseasReceivingAccount.getDocNo()));
        }
        overseasReceivingAccount.setSettlementNo("");
        overseasReceivingAccount.setSettlementValue(overseasReceivingAccount.getSettlementValue().subtract(settlementValue).setScale(2,BigDecimal.ROUND_HALF_UP));
        if(overseasReceivingAccount.getSettlementValue().compareTo(BigDecimal.ZERO) != 0){
            throw new ServiceException(String.format("取消收款单【%s】结汇人民币金额错误",overseasReceivingAccount.getDocNo()));
        }
        overseasReceivingAccount.setIsSettlement(Boolean.FALSE);
        overseasReceivingAccount.setSettlementId(null);
        overseasReceivingAccount.setCustomsRate(null);
        super.updateById(overseasReceivingAccount);
        // 写入订单
        List<OverseasReceivingOrderVO> overseasReceivingOrderVOList = overseasReceivingOrderService.findByAccountId(overseasReceivingAccount.getId());
        BigDecimal totalSettlementValue = BigDecimal.ZERO;
        for(OverseasReceivingOrderVO oraVO : overseasReceivingOrderVOList){
            oraVO.setSettlementValue(oraVO.getAccountValue().divide(overseasReceivingAccount.getWriteValue(),20,BigDecimal.ROUND_HALF_UP).multiply(settlementValue).setScale(2,BigDecimal.ROUND_HALF_UP));
            totalSettlementValue = totalSettlementValue.add(oraVO.getSettlementValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        // 防止分摊不均匀
        overseasReceivingOrderVOList.get(0).setSettlementValue(
                overseasReceivingOrderVOList.get(0).getSettlementValue().add(settlementValue.subtract(totalSettlementValue)).setScale(2,BigDecimal.ROUND_HALF_UP)
        );
        for(OverseasReceivingOrderVO oraVO : overseasReceivingOrderVOList){
            expOrderService.canWriteSettlementValue(oraVO.getExpOrderId(),oraVO.getAccountValue(),oraVO.getSettlementValue());
            // 写入收款单明细结汇人民币金额
            overseasReceivingOrderService.writeSettlementValue(oraVO.getId(),BigDecimal.ZERO);
        }
    }

    @Override
    public Boolean findOrderNoIsSettlement(String docNo) {
        return overseasReceivingAccountMapper.findOrderNoIsSettlement(docNo) == 0;
    }

    @Override
    public OverseasReceivingAccount findByDocNo(String docNo) {
        OverseasReceivingAccount condition = new OverseasReceivingAccount();
        condition.setDocNo(docNo);
        return super.getOne(condition);
    }

    @Override
    public List<ExpOneVoteTheEndVO> oneVoteTheEndByOrderId(Long orderId) {
        return overseasReceivingAccountMapper.oneVoteTheEndByOrderId(orderId);
    }

    /**
     * 通知财务确认
     * @param customer
     */
    public void notice(Customer customer,OverseasReceivingAccount overseasReceivingAccount) {
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.OVERSEASRECEIVINGACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(overseasReceivingAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(customer.getId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.OVERSEAS_RECEIVING_ACCOUNT_CONFIRM.getCode());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单境外收款【%s】需要您确认。",customer.getName(),overseasReceivingAccount.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",overseasReceivingAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    //清空任务通知
    public void clearTaskNotice(Long id){
        taskNoticeContentService.deleteTaskNotice(DomainTypeEnum.OVERSEASRECEIVINGACCOUNT.getCode(), id, "");
    }
}
